package jacktgq.mineSweeper;

import jacktgq.moveTheBox.GameData;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * @Author CandyWall
 * @Date 2021/3/28--12:06
 * @Description 扫雷数据类
 */
public class MineSweeperData {
    public static final BufferedImage BLOCK_IMAGE;
    public static final BufferedImage FLAG_IMAGE;
    public static final BufferedImage MINE_IMAGE;
    public static final BufferedImage[] NUMIMAGES;
    public static final BufferedImage GAMEOVER_IMAGE;
    public static final BufferedImage YOUWIN_IMAGE;

    static {
        BLOCK_IMAGE = readImage("block");
        FLAG_IMAGE = readImage("flag");
        MINE_IMAGE = readImage("mine");
        NUMIMAGES = new BufferedImage[9];
        for (int i = 0; i < 9; i++) {
            NUMIMAGES[i] = readImage(i + "");
        }

        GAMEOVER_IMAGE = readImage("GameOver");
        YOUWIN_IMAGE = readImage("YouWin");


    }

    private static BufferedImage readImage(String filename) {
        try {
            return ImageIO.read(MineSweeperData.class.getResource("/jacktgq/mineSweeper/resources/" + filename + ".png"));
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("读取图片资源失败！");
        }
    }

    private int M, N;
    public boolean[][] isMine;      // 记录格子是否有雷
    public boolean[][] isOpen;      // 记录格子是否被点开
    public boolean[][] isFlag;      // 记录格子上是否插上旗
    private int[][] mineNumberAround;// 记录以当前格子为中心的九宫格中包含的雷的个数
    private boolean gameOver;
    private boolean win;
    private int mineNumber;

    public MineSweeperData(int M, int N, int mineNumber) {
        if (M <= 0 || N <= 0) {
            throw new IllegalArgumentException("游戏区域的行数和列数必须是正整数！");
        }
        if (mineNumber <= 0 || mineNumber > M * N) {
            throw new IllegalArgumentException("雷的数量不能小于0或者大于区域中的格子数！");
        }

        this.M = M;
        this.N = N;
        this.mineNumber = mineNumber;

        isMine = new boolean[M][N];
        isOpen = new boolean[M][N];
        isFlag = new boolean[M][N];
        mineNumberAround = new int[M][N];

        generateMines(mineNumber);
        // 计算以所有格子（有雷的格子除外）为中心的九宫格中包含的雷的个数
        calculateMineNumberAround();
    }

    /**
     * 计算以所有格子（有雷的格子除外）为中心的九宫格中包含的雷的个数
     */
    public void calculateMineNumberAround() {
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                // 如果当前格子中没有雷，统计以这个格子为中的九宫格中雷的个数
                if (!isMine[i][j]) {
                    for (int k = i - 1; k <= i + 1; k++) {
                        for (int l = j - 1; l <= j + 1; l++) {
                            if (inArea(k, l) && isMine[k][l]) {
                                mineNumberAround[i][j]++;
                            }
                        }
                    }
                }
            }
        }
    }

    public int getMineNumberAroundAt(int x, int y) {
        return mineNumberAround[x][y];
    }

    /**
     * 随机生成雷
     */
    private void generateMines(int mineNumber) {
        for (int i = 0; i < mineNumber; i++) {
            int x = i / N;
            int y = i % N;
            isMine[x][y] = true;
        }

        // 随机交换元素的次数
        int swapTimes = 1000;
        // knuth算法：从[0, i+1)区间中随机获取元素，然后和第i个元素交换
        for (int i = M * N - 1; i >= 0; i--) {
            int j = (int) (Math.random() * (i + 1));
            swap(i / N, i % N, j / N, j % N);
        }
    }

    private void swap(int x1, int y1, int x2, int y2) {
        boolean mine = isMine[x1][y1];
        isMine[x1][y1] = isMine[x2][y2];
        isMine[x2][y2] = mine;
    }
    /*private void generateMines(int mineNumber) {
        for (int i = 0; i < mineNumber; i++) {
            int x, y;
            // 如果有雷就一直循环尝试找到一个没有雷的位置
            do {
                x = (int) (Math.random() * M);
                y = (int) (Math.random() * M);
            } while (isMine[x][y]);
            isMine[x][y] = true;
        }
    }*/

    public int getM() {
        return M;
    }

    public int getN() {
        return N;
    }

    public boolean inArea(int x, int y) {
        return x >= 0 && x < M && y >=0 && y < N;
    }

    public boolean isGameOver() {
        return gameOver;
    }

    public boolean isWin() {
        return win;
    }

    /**
     * 游戏重新开始
     */
    public void reset() {
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                isOpen[i][j] = false;
                isMine[i][j] = false;
                isFlag[i][j] = false;
            }
        }
        gameOver = false;
        win = false;
        generateMines(mineNumber);
        // 计算以所有格子（有雷的格子除外）为中心的九宫格中包含的雷的个数
        calculateMineNumberAround();
    }

    /**
     * 打开这个位置以及周围的位置
     * @param x
     * @param y
     */
    public void open(int x, int y) {
        if (isMine[x][y]) {
            throw new IllegalArgumentException("不能打开雷区！");
        }
        isOpen[x][y] = true;

        if (mineNumberAround[x][y] > 0) {
            return;
        }
        for (int i = x - 1; i <= x + 1; i++) {
            for (int j = y - 1; j <= y + 1; j++) {
                if (inArea(i, j) && !isOpen[i][j] && !isMine[i][j]) {
                    open(i, j);
                }
            }
        }
    }

    /**
     * 判断是否赢了
     * @return
     */
    public void checkWin() {
        // 如果还有以下两种情况的任意一种，说明没有赢，反之就赢了
        // 1.没有雷的地方 没有被打开
        // 2.有雷的地方没有插红旗
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                if ((!isMine[i][j] && !isOpen[i][j]) || (isMine[i][j] && !isFlag[i][j])) {
                    win = false;
                    return;
                }
            }
        }
        // 打开所有遮罩
        openAll();

        win = true;
    }

    public void gameOver() {
        gameOver = true;
        // 展开所有
        openAll();
    }

    private void openAll() {
        for (int i = 0; i < M; i++) {
            for (int j = 0; j < N; j++) {
                isOpen[i][j] = true;
            }
        }
    }
}
